//Module name: libups
//Author: Alcaro
//Date: April 4, 2013
//Licence: GPL v3.0 or higher

#include "libups.h"

#ifndef __cplusplus
#include <stdbool.h>//bool; if this file does not exist (hi msvc), remove it and uncomment the following three lines.
//#define bool int
//#define true 1
//#define false 0
#endif
#include <stdint.h>//uint8_t, uint32_t
#include <stdlib.h>//malloc, realloc, free
#include <string.h>//memcpy, memset
#include <stdio.h>//printf
#include "crc32.h"

static uint32_t read32(uint8_t * ptr)
{
    return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
}

#define error(which) do { error = which; goto exit; } while(0)
#define assert_sum(a,b) do { if (SIZE_MAX - (a) < (b)) error(ups_too_big); } while(0)
#define assert_shift(a,b) do { if (SIZE_MAX >> (b) < (a)) error(ups_too_big); } while(0)

enum upserror ups_apply_ex(struct mem patch, struct mem in, struct mem * out, int ignore_crc)
{
    enum upserror error = ups_ok;
    out->len = 0;
    out->ptr = NULL;

    // 声明所有变量在函数开头
    uint8_t *patchat = patch.ptr;
    const uint8_t *patchend = NULL;
    bool backwards = false;
    size_t inlen = 0, outlen = 0;
    uint8_t *inat = NULL;
    const uint8_t *inend = NULL;
    uint8_t *outat = NULL;
    const uint8_t *outend = NULL;
    uint32_t crc_in_expected = 0, crc_out_expected = 0, crc_patch_expected = 0;
    uint32_t crc_in = 0, crc_out = 0, crc_patch = 0;

    if (patch.len < 18) return ups_broken;

    patchend = patch.ptr + patch.len - 12;

    if (patchat[0] != 'U' || patchat[1] != 'P' || patchat[2] != 'S' || patchat[3] != '1') {
        error = ups_broken;
        goto exit;
    }
    patchat += 4;

    // VLV 解码宏
#define decodeto(var) \
    do { \
        var = 0; \
        unsigned int shift = 0; \
        while (true) { \
            if (patchat >= patchend) { error = ups_broken; goto exit; } \
            uint8_t next = *patchat++; \
            size_t addthis = (next & 0x7F); \
            if (SIZE_MAX >> shift < addthis) { error = ups_too_big; goto exit; } \
            var += addthis << shift; \
            if (next & 0x80) break; \
            shift += 7; \
            if (var > SIZE_MAX - (1U << shift)) { error = ups_too_big; goto exit; } \
            var += 1U << shift; \
        } \
    } while(false)

    decodeto(inlen);
    decodeto(outlen);

    if (inlen != in.len) {
        if (outlen == in.len) {
            size_t tmp = inlen;
            inlen = outlen;
            outlen = tmp;
            backwards = true;
        } else {
            error = ups_not_this;
            goto exit;
        }
    }

    out->ptr = (uint8_t*)malloc(outlen);
    if (!out->ptr) { error = ups_out_of_memory; goto exit; }
    out->len = outlen;
    memset(out->ptr, 0, outlen);

    inat = in.ptr;
    inend = in.ptr + in.len;
    outat = out->ptr;
    outend = out->ptr + outlen;

    while (patchat < patchend) {
        size_t skip;
        decodeto(skip);

        while (skip-- > 0) {
            uint8_t byte = (inat < inend) ? *inat++ : 0;
            if (outat < outend) *outat++ = byte;
        }

        uint8_t xor_byte;
        do {
            if (patchat >= patchend) { error = ups_broken; goto exit; }
            xor_byte = *patchat++;
            uint8_t in_byte = (inat < inend) ? *inat++ : 0;
            if (outat < outend) *outat++ = in_byte ^ xor_byte;
        } while (xor_byte != 0);
    }

    if (patchat != patchend) { error = ups_broken; goto exit; }

    while (outat < outend && inat < inend) {
        *outat++ = *inat++;
    }

    // 现在可以安全地计算 CRC
    crc_in_expected = read32(patchat);
    crc_out_expected = read32(patchat + 4);
    crc_patch_expected = read32(patchat + 8);

    crc_in = crc32(in.ptr, in.len);
    crc_out = crc32(out->ptr, out->len);
    crc_patch = crc32(patch.ptr, patch.len - 4);

    if (crc_patch != crc_patch_expected) { error = ups_broken; goto exit; }

    if (!ignore_crc) {
        if (backwards) {
            if (crc_in != crc_out_expected || crc_out != crc_in_expected) {
                error = ups_not_this;
                goto exit;
            }
        } else {
            if (crc_in != crc_in_expected || crc_out != crc_out_expected) {
                error = ups_not_this;
                goto exit;
            }
        }
    }

    // 忽略 CRC 校验时，仍然检查 patch 文件的结构是否完整
    if (ignore_crc && patchat != patchend) {
        error = ups_broken;
        goto exit;
    }

    return ups_ok;

#undef decodeto

exit:
    free(out->ptr);
    out->ptr = NULL;
    out->len = 0;
    return error;
}

// 保持原有接口兼容
enum upserror ups_apply(struct mem patch, struct mem in, struct mem * out)
{
    return ups_apply_ex(patch, in, out, 0);
}

// 变长编码写入函数 (UPS格式)
static bool write_ups_vlv(uint8_t** buf, size_t* pos, size_t* allocated, size_t value) {
    // 确保有足够空间（最多需要5字节）
    if (*pos + 5 > *allocated) {
        size_t new_size = *allocated + 32;
        uint8_t* new_buf = (uint8_t*)realloc(*buf, new_size);
        if (!new_buf) return false;
        *buf = new_buf;
        *allocated = new_size;
    }
    
    // UPS格式的变长编码
    while (1) {
        uint8_t x = value & 0x7F;
        value = value >> 7;
        
        if (value == 0) {
            (*buf)[(*pos)++] = x | 0x80;  // 设置最高位表示结束
            break;
        }
        
        (*buf)[(*pos)++] = x;
        value = value - 1;
    }
    
    return true;
}

enum upserror ups_create(struct mem sourcemem, struct mem targetmem, struct mem* patchmem) {
    const uint8_t UPS_MAGIC[4] = {'U', 'P', 'S', '1'};
    size_t sourcelen = sourcemem.len;
    size_t targetlen = targetmem.len;
    uint32_t crc_in = calc_crc32(sourcemem.ptr, sourcelen);
    uint32_t crc_out = calc_crc32(targetmem.ptr, targetlen);

    // 初始缓冲区大小（魔数 + 文件大小 + 预估差异空间）
    size_t allocated = 64 + sourcelen + targetlen;
    uint8_t* buffer = (uint8_t*)malloc(allocated);
    if (!buffer) return ups_error_memory;
    
    size_t pos = 0;
    size_t previous_end = 1;  // 参考实现中的初始值
    size_t current = 0;

    // 1. 写入魔数
    memcpy(buffer + pos, UPS_MAGIC, 4);
    pos += 4;
    
    // 2. 写入变长编码的文件大小 (UPS格式)
    if (!write_ups_vlv(&buffer, &pos, &allocated, sourcelen) ||
        !write_ups_vlv(&buffer, &pos, &allocated, targetlen)) {
        free(buffer);
        return ups_error_memory;
    }
    
    // 3. 生成差异记录（严格遵循JavaScript实现逻辑）
    while (current < sourcelen || current < targetlen) {
        // 比较当前字节
        uint8_t b1 = (current < sourcelen) ? sourcemem.ptr[current] : 0x00;
        uint8_t b2 = (current < targetlen) ? targetmem.ptr[current] : 0x00;
        
        if (b1 != b2) {
            size_t current_seek = current;
            size_t diff_start = current;
            size_t diff_length = 0;
            
            // 收集连续差异数据
            while (b1 != b2) {
                diff_length++;
                current++;
                
                if (current >= sourcelen && current >= targetlen) break;
                
                b1 = (current < sourcelen) ? sourcemem.ptr[current] : 0x00;
                b2 = (current < targetlen) ? targetmem.ptr[current] : 0x00;
            }
            
            // 计算相对偏移（关键部分）
            size_t relative_offset = current_seek - previous_end + 1;
            previous_end = current_seek + diff_length + 1;
            
            // 写入相对偏移（UPS VLV）
            if (!write_ups_vlv(&buffer, &pos, &allocated, relative_offset)) {
                free(buffer);
                return ups_error_memory;
            }
            
            // 写入XOR数据
            for (size_t i = diff_start; i < diff_start + diff_length; i++) {
                // 确保缓冲区空间
                if (pos >= allocated) {
                    size_t new_size = allocated + 256;
                    uint8_t* new_buf = (uint8_t*)realloc(buffer, new_size);
                    if (!new_buf) {
                        free(buffer);
                        return ups_error_memory;
                    }
                    buffer = new_buf;
                    allocated = new_size;
                }
                
                uint8_t s_byte = (i < sourcelen) ? sourcemem.ptr[i] : 0x00;
                uint8_t t_byte = (i < targetlen) ? targetmem.ptr[i] : 0x00;
                buffer[pos++] = s_byte ^ t_byte;
            }
            
            // 写入差异结束标记（0x00）
            if (pos >= allocated) {
                size_t new_size = allocated + 16;
                uint8_t* new_buf = (uint8_t*)realloc(buffer, new_size);
                if (!new_buf) {
                    free(buffer);
                    return ups_error_memory;
                }
                buffer = new_buf;
                allocated = new_size;
            }
            buffer[pos++] = 0x00;
        } else {
            // 字节相同，继续前进
            current++;
        }
    }
    
    // 4. 写入CRC32校验值（小端序）
    if (pos + 12 > allocated) {
        size_t new_size = allocated + 16;
        uint8_t* new_buf = (uint8_t*)realloc(buffer, new_size);
        if (!new_buf) {
            free(buffer);
            return ups_error_memory;
        }
        buffer = new_buf;
        allocated = new_size;
    }
    
    // 源文件CRC（小端序）
    buffer[pos++] = crc_in & 0xFF;
    buffer[pos++] = (crc_in >> 8) & 0xFF;
    buffer[pos++] = (crc_in >> 16) & 0xFF;
    buffer[pos++] = (crc_in >> 24) & 0xFF;
    
    // 目标文件CRC（小端序）
    buffer[pos++] = crc_out & 0xFF;
    buffer[pos++] = (crc_out >> 8) & 0xFF;
    buffer[pos++] = (crc_out >> 16) & 0xFF;
    buffer[pos++] = (crc_out >> 24) & 0xFF;
    
    // 5. 计算并写入补丁CRC32（注意：计算不包括最后的4字节）
    uint32_t patch_crc = calc_crc32(buffer, pos);
    buffer[pos++] = patch_crc & 0xFF;
    buffer[pos++] = (patch_crc >> 8) & 0xFF;
    buffer[pos++] = (patch_crc >> 16) & 0xFF;
    buffer[pos++] = (patch_crc >> 24) & 0xFF;
    
    // 6. 返回最终结果
    patchmem->ptr = buffer;
    patchmem->len = pos;
    
    return ups_ok;
}

void ups_free(struct mem mem)
{
	free(mem.ptr);
}

#if 0
//Sorry, no undocumented features here. The only thing that can change an UPS patch is swapping the two sizes and checksums, and I don't create anyways.
#endif
